﻿/*
*author:liujg1015@gmail.com
*createDate:2012.6.13
*version:1.0.0
*/
(function($) {
    var mailbox = function(srcElement, options) {
        this.currli = false;
        this.container = false;        
        this.trigger = srcElement;
        this.dic = [];
        this.source = false;
        this.isShow = false;
        this.isMouseIn = false;
        this.fieldName = false;
        this.mails = [];
        this.mailFormat = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/i;
        this.mailNode = '<span style="float:left;line-height:22px;margin-right:5px;cursor:pointer;" title="单击移除">$text$,<input type="hidden" name="$name$" value="$value$"/></span>';
        this.setting = {
            containerId: '', //列表项容器id
            currentItemCss: 'curr', //当前列表项的样式
            isFuzzyMatch: true, //模糊匹配
            isLazyMatch: false, //贪婪匹配
            fnCreateCode: false//自定义创建code的函数,自定义函数中必须返回值
        };
        $.extend(this.setting, options);
        this.init();
    };
    mailbox.prototype = {
        init: function() {
            var _this = this;
            this.container = $('#' + this.setting.containerId);

            this.source = this.container.children().clone(true);
            this.source.each(function(i, o) {
                var _code = false;
                if(_this.setting.fnCreateCode) {
                    _code = _this.setting.fnCreateCode.call(null, $.trim(o.innerHTML));
                }
                else {
                    _code = o.code;
                }
                if(!_code) {
                    _code = '';
                }

                var _lazyCode = [];
                var _pattern = /[A-Z]+/g;
                while((_head = _pattern.exec(_code)) != null) {
                    _lazyCode.push(_head);
                }
                _this.dic.push({ key: $.trim(o.innerHTML), code: _code, lazyCode: _lazyCode.join(''), val: $.trim($(o).attr('val')) });
            });

            this.bindEventToDocument();
            this.bindEventToTrigger();            
            this.container.empty().html('');
        },
        bindEventToDocument: function() {
            var _this = this;

            this.container.mouseover(function() { _this.isMouseIn = true }).mouseout(function() { _this.isMouseIn = false });

            $('body').click(function() {
                if(_this.isShow && !_this.isMouseIn) {
                    _this.close();
                }
            });

            $('form').has(this.trigger).bind('submit', function() {
                _this.setSelectedVal();
            });

            $(document).keydown(function(event) {
                if(event.keyCode == 13) {
                    event.stopPropagation();
                    event.preventDefault();
                    return false;
                }
            });
        },
        bindEventToTrigger: function() {
            var _this = this;
            this.trigger.keydown(function(event) {
                var _keycode = event.keyCode;
                if(_keycode == 40) {
                    if(_this.isShow) {
                        _this.moveDown();
                        return;
                    }
                    _this.loadAll();
                }
                if(_keycode == 38) {
                    if(_this.isShow) {
                        _this.moveUp();
                        return;
                    }
                    _this.loadAll();
                }
            });

            this.trigger.keyup(function(event) {

                var _keyCode = event.keyCode;

                if(_this.isShow) {

                    if(_keyCode == 40 || _keyCode == 38) {
                        return;
                    }

                    if(_keyCode == 13) {
                        if(_this.currli) {
                            _this.currli.click();
                        }
                        else {
                            _this.close();
                        }
                        return;
                    }

                    if(_keyCode == 27 || (_keyCode == 8 && $.trim(this.value) == '')) {
                        _this.close();
                        return;
                    }
                }

                if(_keyCode == 13) {
                    _this.setSelectedVal();
                    return;
                }

                if(_keyCode == 8 && this.value == '') {
                    _this.removeMail();
                    return;
                }

                if($.trim(this.value).length == 0) {
                    return;
                }
                else {
                    _this.search(this.value);
                }
                return false;
            });
        },
        bindEventToItem: function() {
            var _this = this;
            this.container.children().click(function() {
                _this.trigger.val($.trim(this.innerHTML));
                _this.close();
            }).hover(function() {
                _this.currli = $(this);
                _this.currli.siblings().removeClass(_this.setting.currentItemCss);
                _this.currli.addClass(_this.setting.currentItemCss).focus();
            });
        },
        setSelectedVal: function() {
            var _isGot = false;
            var _inputstr = $.trim(this.trigger.val());
            var _val = false;
            if(_inputstr.length > 0) {
                for(var i = 0; i < this.dic.length; i++) {
                    var _item = this.dic[i];
                    if(_inputstr == _item.key) {
                        _isGot = true;
                        _val = _item.val;
                        break;
                    }
                }
            }
            if(!_val) {
                if(this.mailFormat.test(_inputstr)) {
                    _val = _inputstr;
                }
            }
            if(_val) {
                this.trigger.val('');
                if($.inArray(_val, this.mails) >= 0) {
                    return;
                }
                var _node = this.mailNode.replace('$text$', _inputstr);
                _node = _node.replace('$name$', this.trigger.attr('name'));
                _node = _node.replace('$value$', _val);
                var _this = this;
                _node = $(_node).click(function() { _this.removeMail(this) })
                this.trigger.before(_node);
                this.mails.push(_val);
            }
            return _isGot;
        },
        removeMail: function(el) {
            if(!el) {
                el = this.trigger.prev();
            }
            else {
                el = $(el);
            }
            var _val = $(':hidden', el).val();
            var _index = $.inArray(_val, this.mails);
            if(_index >= 0) {
                for(_index; _index < this.mails.length - 1; _index++) {
                    this.mails[_index] = this.mails[_index + 1];
                }
                this.mails.pop();
                el.remove();
            }
        },
        loadAll: function() {
            this.container.html(this.source.clone(true));
            this.bindEventToItem();
            var _left = this.trigger.position().left;
            var _top = this.trigger.position().top + 13;
            this.container.css({ left: _left + 'px', top: _top + 'px' }).show();
            this.trigger.focus();
            this.isShow = true;
        },
        doSearch: function(str, isGreedy) {
            var _val = str.toLowerCase();
            var _len = str.length;
            var _fuzzyPattern = new RegExp(str.replace('[', '\[').replace(']', '\]'), 'i');
            var _result = {};
            var _greedyResult = [];

            for(var i = 0; i < this.dic.length; i++) {
                var _item = this.dic[i];
                var _isFound = false;
                if(this.setting.isFuzzyMatch) {
                    if(_fuzzyPattern.test(_item.key) || _fuzzyPattern.test(_item.code) || (this.setting.isLazyMatch && _fuzzyPattern.test(_item.lazyCode))) {
                        _isFound = true;
                    }
                }
                else {
                    var _key = _item.key.substr(0, _len).toLowerCase();
                    var _code = false;
                    if(_item.code && _item.code.length >= _len) {
                        _code = _item.code.substr(0, _len).toLowerCase();
                    }

                    var _lazyCode = false;
                    if(this.setting.isLazyMatch && _item.lazyCode && _item.lazyCode.length >= _len) {
                        _lazyCode = _item.lazyCode.substr(0, _len).toLowerCase();
                    }

                    if(_key == _val || _code == _val || _lazyCode == _val) {
                        _isFound = true;
                    }
                }
                if(_isFound) {
                    if(isGreedy) {
                        _greedyResult.push({ index: i, item: _item });
                    }
                    else {
                        _result = { index: i, item: _item }
                        break;
                    }
                }
            }

            if(isGreedy) {
                return _greedyResult;
            }
            return _result;
        },
        search: function(str) {
            str = $.trim(str);
            if(str == '') {
                return;
            }
            var _result = this.doSearch(str, true);
            if(_result.length > 0) {
                this.container.empty();
                for(var i = 0; i < _result.length; i++) {
                    this.container.append(this.source.eq(_result[i].index));
                }
                this.bindEventToItem();
                this.show(true);
            }
            else if(this.isShow) {
                this.close();
            }
        },
        scroll: function() {
            if(this.currli) {
                var _top = this.currli.position().top;
                if(_top < 0) {
                    var _sT = this.container.scrollTop() + _top;
                    this.container.scrollTop(_sT);
                }
                else {
                    var _mainH = this.container.height();
                    var _myTop = _top + this.currli.height();
                    if(_myTop > _mainH) {
                        var _sT = this.container.scrollTop() + (_myTop - _mainH);
                        this.container.scrollTop(_sT);
                    }
                }
            }
        },
        moveDown: function() {
            this.container.children().removeClass(this.setting.currentItemCss);
            if(!this.currli) {
                this.currli = this.container.children(':first-child');
            }
            else if(this.currli.index() != this.container.children(':last-child').index()) {
                this.currli = this.currli.next();
            }
            else {
                this.currli = false;
                this.moveDown();
            }
            if(this.currli) {
                this.currli.addClass(this.setting.currentItemCss).focus();
                this.scroll();
            }
        },
        moveUp: function() {
            this.container.children().removeClass(this.setting.currentItemCss);
            if(!this.currli) {
                this.currli = this.container.children(':last-child');
            }
            else if(this.currli.index() != this.container.children(':first-child').index()) {
                this.currli = this.currli.prev();
            }
            else {
                this.currli = false;
                this.moveUp();
            }
            if(this.currli) {
                this.currli.addClass(this.setting.currentItemCss).focus()
                this.scroll();
            };
        },
        show: function(keyCode) {
            if(arguments.length == 0) {
                if(this.isShow) {
                    this.close();
                }
                else {
                    this.loadAll();
                }
                return;
            }
            if(!this.isShow) {
                var _left = this.trigger.position().left;
                var _top = this.trigger.position().top + 13;
                this.container.css({ left: _left + 'px', top: _top + 'px' }).show();
                this.isShow = true;
            }
            this.isShow = true;
        },
        close: function() {
            this.isShow = false;
            this.currli = false;
            this.container.hide();
            this.container.children().removeClass(this.setting.currentItemCss);
            this.container.empty();
            this.setSelectedVal();
        }
    };

    $.fn.extend({
        mailbox: function(options) {
            var obj = new mailbox(this, options);
            return obj;
        }
    });
})(jQuery)